/*
 * @(#)Function.java        1.0 19-Apr-2009
 *
 * Copyright 2008 Thomas Ford. All rights reserved.
 */


package edu.napier.soc.xfdm.model;

import java.io.Serializable;
import java.util.List;

/**
 * The {@code Function} class represents a function in the FDM. A
 * {@code Function} is not bound to any specific {@code EntityType} and can be
 * shared across many types.<p />
 *
 * Note: This class is immutable and therefore <strong>thread-safe</strong>.
 *
 * @version 1.0, 17-Apr-2009
 * @author  Thomas Ford
 */
public class Function implements Serializable {

	private static final long serialVersionUID = 7685352495674l;

	private final String name;
	private final ReturnType returnType;
	private final boolean isMultiValued;

	/**
	 * Returns a new {@code Function} which returns multiple {@code Entities}.
	 *
	 * @param  name
	 *         the name of the {@code Function}.
	 *
	 * @param  entityType
	 *         the {@code Entity Type} to return.
	 *
	 * @return the constructed {@code Function}.
	 */
	public static Function newMultiEntity(String name,
			EntityType entityType) {
		return new EntityFunction(name, entityType, true);
	}

	/**
	 * Returns a new {@code Function} which returns a single {@code Entities}.
	 *
	 * @param  name
	 *         the name of the {@code Function}.
	 *
	 * @param  entityType
	 *         the {@code Entity Type} to return.
	 *
	 * @return the constructed {@code Function}.
	 */
	public static Function newSingleEntity(String name,
			EntityType entityType) {
		return new EntityFunction(name, entityType, false);
	}

	/**
	 * Returns a new {@code Function} which a single primitive data type.
	 *
	 * @param  name
	 *         the name of the {@code Function}.
	 *
	 * @param  returnType
	 *         the primitive data type to return.
	 *
	 * @return the constructed {@code Function}.
	 */
	public static Function newSinglePrimitive(String name,
			ReturnType returnType) {
		return new Function(name, returnType, false);
	}

	Function(String name, ReturnType returnType, boolean isMultiValued) {
		if (name == null) {
			throw new NullPointerException("name must not be null.");
		}
		if (returnType == null) {
			throw new NullPointerException("returnType must not be null.");
		}
		this.name = name;
		this.returnType = returnType;
		this.isMultiValued = isMultiValued;
	}

	/**
	 * Checks if {@code value} is of the correct return type. If {@code value}
	 * is a {@code List} then each element inside the {@code List} is checked
	 * recursively.
	 *
	 * @param  value
	 *         the value to be checked for validity.
	 *
	 * @return {@code true} if {@code value} is valid, otherwise {@code false}.
	 */
	public boolean validateValue(Object value) {
		if (value instanceof List) {
			return validateMulti((List)value);
		} else if (value == null) {
			return true;
		} else {
			return isValidType(value);
		}
	}

	private boolean validateMulti(List list) {
		if (!isMultiValued) {
			return false;
		}

		for (Object o : list) {
			if (!isValidType(o)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * @return {@code true} if thia {@code Function} returns an
	 *         {@code Entity Type}, otherwise {@code false}.
	 */
	public boolean isEntityReturn() {
		return returnType == ReturnType.ENTITY;
	}

	/**
	 * @return the name of this {@code Function}.
	 */
	public String getName() {
		return name;
	}

	/**
	 * @return the return type of this {@code Function}.
	 */
	public ReturnType getReturnType() {
		return returnType;
	}

	/**
	 * @return {@code true} if this {@code Function} returns multiple values.
	 */
	public boolean isMultiValued() {
		return isMultiValued;
	}

	protected boolean isValidType(Object value) {
		switch(returnType) {
			case STRING:
				return value instanceof String;
			case INTEGER:
				return value instanceof Integer;
			case BOOLEAN:
				return value instanceof Boolean;
			case REAL:
				return value instanceof Double;
			case CHAR:
				return value instanceof Character;
			default:
				return false;
		}
	}

	@Override
	public String toString() {
		return name;
	}
}
